﻿using Media.Hepler;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

namespace Media.Models
{
    public class ImageRepo
    {
        private string _storpath = HttpContext.Current.Server.MapPath("/") + "TN_CMS.Media/";

        public bool SaveImage(string folder, string fileName, string localFileName)
        {
            string sourceFile = localFileName;
            string destinationFile = _storpath + folder + "/Original/" + fileName;
            
            string fileType ="";
            GetImageType(fileName, out fileType);
            //Check allowed file type and not existed file
            if (fileType != null && !File.Exists(destinationFile))
            {
                // To move a file or folder to a new location:
                System.IO.File.Move(sourceFile, destinationFile);
                return true;
            }

            return false;
        }

        public byte[] GetImage(string folder, string fileName, out string strFileType, bool isCrop = false, bool needToFill = true)
        {
            ImageFormat fileType = GetImageType(fileName, out strFileType);

            //if the file type invalid
            if (fileType == null)
                return GetNoImage(out strFileType);
            string originalImage = "";
            string fullFilePath = _storpath + folder + "/Original/";
            Bitmap image = null;
            MemoryStream ms = new MemoryStream();
            int firstUnderline = fileName.IndexOf('_');
            
            //request for original file
            if (firstUnderline == -1)
            {
                image = Image.FromFile(fullFilePath + fileName) as Bitmap;
                image.Save(ms, fileType);
                return ms.ToArray();
            }

            originalImage += fileName.Substring(0, firstUnderline) + "." + strFileType; // get image.jpg from image_w200.jpg or image_h200 or image_w200_h100.jpg
            fullFilePath += originalImage;

            //the file is not exist
            if (!File.Exists(fullFilePath))
            {
                return GetNoImage(out strFileType);
            }
            image = Image.FromFile(fullFilePath) as Bitmap;
            int sourceWidth = image.Width;
            int sourceHeight = image.Height;
            int width = 0;
            int height = 0;

            //get width and height from fileName, return null if they are invalid
            if (!GetImageSize(fileName, out width, out height, sourceWidth, sourceHeight))
            {
                return GetNoImage(out strFileType);
            }

            int sourceX = 0;
            int sourceY = 0;
            int destX = 0;
            int destY = 0;
            float nPercent = 1;
            int destWidth = 0;
            int destHeight = 0;

            int cropWidth = 0;
            int cropHight = 0;

            //we have both width and height in request file name
            if (width > 0 && height > 0)
            {
                isCrop = true;
                nPercent = CalculatePercentage(width, height, sourceWidth, sourceHeight, needToFill, out destX, out destY);
                destWidth = width;
                destHeight = height;
                cropWidth = (int)Math.Round(sourceWidth * nPercent);
                cropHight = (int)Math.Round(sourceHeight * nPercent);
            }
            //we only have width or height
            else
            {
                isCrop = false;
                nPercent = CalculatePercentage(width, height, sourceWidth, sourceHeight, needToFill);
                destWidth = (int)Math.Round(sourceWidth * nPercent);
                destHeight = (int)Math.Round(sourceHeight * nPercent);
            }
            
            width = width <= 0 ? sourceWidth : width;
            height = height <= 0 ? sourceHeight : height;

            //get the file name width size to store
            string storeImageName = GenerateImageName(folder, originalImage, width, height, strFileType, fileType);
            //return if we generated it before
            if (File.Exists(storeImageName))
            {
                image = Image.FromFile(storeImageName) as Bitmap;
                image.Save(ms, fileType);
                return ms.ToArray();
            }

            Bitmap bmPhoto = new Bitmap( destWidth, destHeight, PixelFormat.Format32bppRgb);
            bmPhoto.SetResolution(image.HorizontalResolution, image.VerticalResolution);

            Graphics grPhoto = Graphics.FromImage(bmPhoto);
            grPhoto.Clear(System.Drawing.Color.White);

            if (nPercent > 0.50)
                grPhoto.InterpolationMode = InterpolationMode.Bilinear;
            else if (nPercent > 0.25)
                grPhoto.InterpolationMode = InterpolationMode.Bicubic;
            else
                grPhoto.InterpolationMode = InterpolationMode.High;

            grPhoto.CompositingQuality = CompositingQuality.Default;
            grPhoto.SmoothingMode = SmoothingMode.HighQuality;

            if (isCrop)
            {
                grPhoto.DrawImage(image, destX, destY, cropWidth, cropHight);
            }
            else
            {
                grPhoto.DrawImage(image, sourceX, sourceY, destWidth, destHeight);
            }

            grPhoto.Dispose();
            //bmPhoto.Save(
            bmPhoto.Save(storeImageName, fileType);
            bmPhoto.Save(ms, fileType);
            bmPhoto.Dispose();
            return  ms.ToArray();
        }

        #region Support GetImage()

        /// <summary>
        /// Get no-image when cannot find image from request
        /// </summary>
        /// <returns></returns>
        private byte[] GetNoImage(out string fileType)
        {
            fileType = "jpg";
            string fullFilePath = HttpContext.Current.Server.MapPath("/") + "TN_CMS.Media/Default/no-image.jpg";
            Bitmap image = Image.FromFile(fullFilePath) as Bitmap;

            MemoryStream ms = new MemoryStream();
            image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
            return ms.ToArray();
        }

        private float CalculatePercentage(int width, int height, int sourceWidth, int sourceHeight, bool needToFill)
        {
            int destX = 0;
            int destY = 0;
            return CalculatePercentage(width, height, sourceWidth, sourceHeight, needToFill, out  destX, out  destY);
        }

        private float CalculatePercentage(int width, int height, int sourceWidth, int sourceHeight, bool needToFill,out int destX, out int destY)
        {
            float nPercent = 0;
            float nPercentW = 0;
            float nPercentH = 0;
            destX = 0;
            destY = 0;

            nPercentW = ((float)width / (float)sourceWidth);
            nPercentH = ((float)height / (float)sourceHeight);

            nPercentW = nPercentW > 0 ? nPercentW : 0;
            nPercentH = nPercentH > 0 ? nPercentH : 0;

            if (!needToFill)
            {
                if (nPercentH < nPercentW )
                {
                    nPercent = nPercentH;
                }
                else
                {
                    nPercent = nPercentW;
                }
            }
            else
            {
                if (nPercentH > nPercentW)
                {
                    nPercent = nPercentH;
                    destX = (int)Math.Round((width - (sourceWidth * nPercent)) / 2);
                }
                else
                {
                    nPercent = nPercentW;
                    destY = (int)Math.Round((height - (sourceHeight * nPercent)) / 2);
                }
            }
            return (nPercent > 1 || nPercent <=0 ) ? 1 : nPercent;
        }

        /// <summary>
        /// Check if this image is allow to store
        /// </summary>
        /// <param name="type">image file extension</param>
        /// <returns></returns>
        private bool IsAllowedImage(string type)
        {
            string[] allowTypes = TN_CMSMediaConfig.GetSettingByKey("Image.MIME").Split(',');
            return allowTypes.Contains(type);
        }

        /// <summary>
        /// Extract image size form file name
        /// </summary>
        /// <param name="fullFileName">file name, has orifinal_fileName_size format, e.g. apple.jpg, apple_w200.jpg, apple_h200.jpg, apple_w200_h200.jpg</param>
        /// <param name="width">width form image name</param>
        /// <param name="height">height form image name</param>
        /// <param name="originalWidth">width of original image file</param>
        /// <param name="originalHeight">height of original image file</param>
        /// <returns>true if size is valid, and false otherwise</returns>
        private bool GetImageSize(string fullFileName, out int width, out int height, int originalWidth, int originalHeight)
        {
            int lastDotIndex = fullFileName.LastIndexOf('.');
            width = height = -1;

            //invalid file name
            if (lastDotIndex == -1)
                return false;

            int firstUnderlineIndex = fullFileName.LastIndexOf('_');
            int lastUnderlineIndex = fullFileName.LastIndexOf('_');

            string fnameWOType = fullFileName.Substring(0, lastDotIndex); //file name without file extension

            string[] segments = fnameWOType.Split('_');

            //no define custom size in file name, return original size
            if (segments.Count() == 1)
            {
                width = originalWidth;
                height = originalHeight;
                return true;
            }

            //have width or height
            else if (segments.Count() == 2)
            {
                if (!GetSize(segments[1], out width, out height))
                {
                    return false;
                }

                //maintain aspect ration
                //width = width == -1 ? originalWidth : width;
                //height = height == -1 ? originalHeight : height;

                return true;
            }

            //have both width and height
            else if (segments.Count() == 3) 
            {
                return GetSize(segments[1], segments[2], out width, out height);
            }

            //wrong format
            else 
            {
                return false;
            }
        }

        /// <summary>
        /// Get width and height form segment
        /// </summary>
        /// <param name="size1">first segment, i.e "w200", "h200"</param>
        /// <param name="size2">second segment, i.e "w200", "h200"</param>
        /// <param name="width">out, extracted width</param>
        /// <param name="height">out, extracted height</param>
        /// <returns>true if size is valid for all widht and height, and false otherwise</returns>
        private bool GetSize(string size1, string size2, out int width, out int height)
        {
            width = height = -1;
            int tmpWidth = -1, tmpHeight = -1;
            //check size 1
            GetSize(size1, out width, out height);
            GetSize(size2, out tmpWidth, out tmpHeight);

            width = width < tmpWidth ? tmpWidth : width;
            height = height < tmpHeight ? tmpHeight : height;

            if (width <= 0 || height <= 0)
                return false;
            return true;
        }

        /// <summary>
        /// Get width or height form segment
        /// </summary>
        /// <param name="size">size string, i.e "w200", "h200"</param>
        /// <param name="width">out, extracted width</param>
        /// <param name="height">out, extracted height</param>
        /// <returns>true if size is valid for width or height, and false otherwise</returns>
        private bool GetSize(string size, out int width, out int height)
        {
            width = height = -1;

            if (size.IndexOf('w') == 0)
            {
                Int32.TryParse(size.Substring(1), out width);
                if (width <= 0)
                {
                    return false;
                }
                return true;
            }
            else if (size.IndexOf('h') == 0)
            {
                Int32.TryParse(size.Substring(1), out height);
                if (height <= 0)
                {
                    return false;
                }
                return true;
            }
            return false;
        }

        private string GenerateImageName(string folder, string originalName, int width, int height, string fileType, ImageFormat format)
        {
            int lastDot = originalName.LastIndexOf('.');
            string name = originalName.Substring(0, lastDot);

            if (width > 0)
                name += "_w" + width;
            if (height > 0)
                name += "_w" + height;
            return _storpath + folder + "/" + name + "." + fileType;
        }

        #endregion

        private ImageFormat GetImageType(string fileName, out string strFileType)
        {
            int lastDot = fileName.LastIndexOf('.');
            if (lastDot == -1 || (lastDot + 1) == fileName.Length)
            {
                strFileType = null;
                return null;
            }
            string type = fileName.Substring(lastDot + 1).ToLower();

            switch (type)
            {
                case "jpeg":
                    strFileType = "jpeg";
                    return ImageFormat.Jpeg;
                case "jpg":
                    strFileType = "jpg";
                    return ImageFormat.Jpeg;
                case "gif":
                    strFileType = "gif";
                    return ImageFormat.Gif;
                case "png":
                    strFileType = "png";
                    return ImageFormat.Png;
                case "ico":
                    strFileType = "ico";
                    return ImageFormat.Icon;
                case "icon":
                    strFileType = "icon";
                    return ImageFormat.Icon;
                default:
                    strFileType = null;
                    return null;
            }
        }
    }
}